package algo3c3g3.model;

import algo3c3g3.model.action.*;
import java.util.*;

import org.jdom.Element;

import algo3c3g3.model.persistence.Persistence;
import algo3c3g3.model.persistence.Persistable;

/**
 * Conjunto de acciones disponibles en un nivel. Contiene los distintos tipos de acciones 
 * disponibles y su cantidad.
 */
public class ActionsAvailable extends Observable implements Persistable
{
    private Set<ActionQuantity> actions = new HashSet<ActionQuantity>();
    
    
    public ActionsAvailable() {}
    
    public ActionsAvailable(Element e)
    {
        this.load(e);
    }

    /**
     * Agrega un nuevo tipo de accion y su cantidad disponible. No permite ingresar la misma
     * accion mas de una vez, sin importar las cantidades.
     * Devuelve true si la accion se pudo insertar.
     */
    public boolean addAction(Class actionClass, int quantity)
    {
        return actions.add(new ActionQuantity(actionClass.getName(), quantity));
    }
        
    /**
     * Recupera una accion del tipo de accion indicado si hay cantidad disponible.
     * La cantidad es reducida en uno.
     * @return Cantidad de acciones disponibles para el tipo de accion indicado.
     * Si no quedan devuelve 0.
     */
    public int fetchAction(Class actionClass)
    {
        ActionQuantity a = find(actionClass);
        
        if(a != null)
        {
            int qty = a.fetch(actionClass.getName());
            setChanged();
            notifyObservers();
            return qty;
        }
        
        return 0;
    }
    
    /**
     * Devuelve la cantidad disponible para el tipo de accion indicado.
     * Si no hay cantidad disponible devuelve 0.
     */
    public int getQuantity(Class actionClass)
    {   
        ActionQuantity a = find(actionClass);
        
        if(a != null)
            return a.getQuantity();
        
        return 0;
    }
    
    /*
     * Recorre el set de acciones buscando la accion indicada.
     * Si existe deuvelve la referencia. Sino devuelve null.
     */
    private ActionQuantity find(Class actionClass)
    {
        Iterator<ActionQuantity> i = actions.iterator();
    	
    	ActionQuantity a = null;
    	
    	while(i.hasNext())
    	{
            a = i.next();
            
            if(a.getClassName().equals(actionClass.getName()))
                return a;
        }
        
        return null;
    }
    
    public Element save()
    {
        Element root = new Element(this.getClass().getCanonicalName());

        Element e = new Element("actions");

        Iterator<ActionQuantity> i = this.actions.iterator();

        while (i.hasNext())
        {
            ActionQuantity aq = i.next();
            e.addContent(aq.save());
        }

        root.addContent(e);

        return root;
    }

    public void load(Element e)
    {
        Element elementActions = e.getChild("actions");

        Iterator<Element> i = elementActions.getChildren().iterator();

        while (i.hasNext())
        {
            this.addAction(new ActionQuantity(i.next()));
        }
    }

    private void addAction(ActionQuantity aq)
    {
        actions.add(aq);
    }
    
    
    /*
     * Representa la cantidad de acciones disponible para un cierto tipo de accion.
     */
    private static class ActionQuantity implements Persistable
    {
    	private String className;
        private int quantity;

        public ActionQuantity(String className, int qty)
        {
            this.className = new String(className);
            quantity = qty;
        }

        public ActionQuantity(Element e)
        {
            this.load(e);
        }

        public int getQuantity()
        {
            return quantity;
        }

        public String getClassName() { return className; }
        
        /*
         * Si hay cantidad disponible la resta en uno y devuelve la cantidad
         */
        public int fetch(String name)
        {
            if(quantity > 0)
                quantity--;

            return quantity;
        }
        
        @Override
        public boolean equals(Object o)
        {
            ActionQuantity a = (ActionQuantity) o;
            return this.getClassName().equals(a.getClassName());
        }
        
        @Override
        public int hashCode()
        {
            int hash = 7;
            hash = 83 * hash + (this.className != null ? this.className.hashCode() : 0);
            return hash;
        }

        public void load(Element e)
        {
            this.className = e.getChildText("className");
            this.quantity = new Integer(e.getChildText("quantity")).intValue();
        }

        public Element save()
        {
            Element root = new Element(this.getClass().getCanonicalName());

            Element e = new Element("className");
            e.addContent(this.className);
            root.addContent(e);

            e = new Element("quantity");
            e.addContent(Integer.toString(this.quantity));
            root.addContent(e);

            return root;
        }
    }
    
    
    
    public static void main(String[] args)
    {
        System.out.println();
        
        ActionsAvailable actions = new ActionsAvailable();
        
        System.out.println("Insertando 10 Walkers: " + actions.addAction(Walker.class, 10));
        System.out.println("Insertando 2 Bashers: " + actions.addAction(Basher.class, 2));
        System.out.println("Insertando 5 Floaters: " + actions.addAction(Floater.class, 5));
        // Inserta una accion duplicada. El Set se ocupa de no insertar duplicados.
        System.out.println("Insertando 5 Floaters de nuevo: " + actions.addAction(Floater.class, 5));
        System.out.println("Insertando 1 Walker de nuevo: " + actions.addAction(Walker.class, 1));
        
        System.out.println();
        
        System.out.println("Sacando un Walker: " + actions.fetchAction(Walker.class));
        System.out.println("Sacando un Walker: " + actions.fetchAction(Walker.class));
        System.out.println("Sacando un Basher: " + actions.fetchAction(Basher.class));
        // Saca una accion que no existe.
        System.out.println("Sacando un Builder: " + actions.fetchAction(Builder.class));
        
        System.out.println();
        
        System.out.println("Cantidad de Walkers: " + actions.getQuantity(Walker.class));
        // Cantidad de una accion que no existe.
        System.out.println("Cantidad de Builders: " + actions.getQuantity(Builder.class));
        
        System.out.println();
        
        Persistence.toXMLFile(actions.save(), "C:\\salida.xml");
        Persistence.toXMLFile(new ActionsAvailable(
        		Persistence.getRootFromXMLFile("C:\\salida.xml")).save(),
        		"C:\\salida2.xml");
    }
}
